﻿using System;
using System.Collections.Generic;
using Fabrication.Exceptions;
using Fabrication.ValueProviders;

namespace Fabrication
{
    public class FabricationScope
    {
        private readonly Dictionary<Type, IBuilderOptions> allBuilderOptions = new Dictionary<Type, IBuilderOptions>();
        private readonly ConventionsProvider conventionsProvider = new ConventionsProvider();

        public IBuilderOptions<T> Setup<T>()
        {
            var type = typeof(T);

            // TODO: Is this the right exception to throw here?
            if (type.IsInterface || type.IsAbstract)
                throw new ItemBuilderException("Top level class cannot be an interface or abstract class");

            IBuilderOptions options;
            if (allBuilderOptions.TryGetValue(type, out options))
                return options as IBuilderOptions<T>;

            options = new BuilderOptions<T>(conventionsProvider);
            allBuilderOptions.Add(type, options);
            return options as IBuilderOptions<T>;
        }

        public T Generate<T>()
        {
            var options = Setup<T>();
            return options.Generate();
        }

        public IEnumerable<T> Generate<T>(int numberOfItems)
        {
            var options = Setup<T>();
            return options.Generate(numberOfItems);
        }

        public IEnumerable<T> Generate<T>(int minimumNumberOfItems, int maximumNumberOfItems)
        {
            var options = Setup<T>();
            return options.Generate(minimumNumberOfItems, maximumNumberOfItems);
        }

        public void SetConvention<TParam, TProvider>()
           where TProvider : IValueProvider
        {
            conventionsProvider.SetConvention<TParam, TProvider>();
        }

        public void SetConvention<TPropertyType, TProvider>(string propertyName)
            where TProvider : IValueProvider
        {
            var provider = (IValueProvider)Activator.CreateInstance(typeof(TProvider));
            SetConvention<TPropertyType>(provider, propertyName);
        }

        public void SetConvention<TPropertyType>(IValueProvider provider, string propertyName)
        {
            conventionsProvider.SetConvention<TPropertyType>(provider, propertyName);
        }

        public void RemoveConvention(string propertyName)
        {
            conventionsProvider.RemoveConvention(propertyName);
        }

        public void RemoveConvention<TPropertyType>(string propertyName)
        {
            conventionsProvider.RemoveConvention<TPropertyType>(propertyName);
        }

        public void RemoveAllNamedConventions()
        {
            conventionsProvider.RemoveAllNamedConventions();
        }

        internal void ClearOptionsFor<T>()
        {
            allBuilderOptions.Remove(typeof(T));
        }
    }
}